package com.social.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.social.dao.ActivityDao;
import com.social.pojo.*;
import com.social.pojo.dto.ActivityDTO;
import com.social.pojo.dto.UserDTO;
import com.social.pojo.vto.ActivityVTO;
import com.social.service.ActivityRegisterService;
import com.social.service.ActivityService;
import com.social.service.MenuService;
import com.social.utils.*;

import org.apache.commons.math3.analysis.function.Min;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.sql.Time;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.social.constant.MessageConstant.*;
import static com.social.constant.RedisConstants.*;
import static com.social.constant.StateConstant.ACTIVITY_REGISTER_STATE_FAIL;
import static com.social.constant.StateConstant.ACTIVITY_REGISTER_STATE_SUCCESS;

/**
 * @className: ActivityServiceImpl
 * @description: TODO 类描述
 * @author: XiaoXu
 * @date: 2022/10/09 19:16
 **/
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityDao, Activity> implements ActivityService {


    @Autowired
    private SysUserServiceImpl userService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private VolunteerServiceImpl volunteerService;
    @Resource
    private CacheClient cacheClient;

    @Resource
    private MenuService menuService;

    private ActivityRegisterService activityRegisterService;
    @Autowired
    public void setActivityRegisterService(ActivityRegisterService activityRegisterService){
        this.activityRegisterService = activityRegisterService;
    }

    @Override
    public Result insert(Activity activity) {
        activity.setPrincipalId(UserHolder.getUser().getId());
        activity.setPublicTime(LocalDateTime.now());
        boolean success = save(activity);
        if(!success) {
            return Result.fail(SAVA_DATA_FAIL,"添加失败");
        }
        RankData rank = BeanUtil.copyProperties(activity, RankData.class);
        stringRedisTemplate.opsForZSet().add(ACTIVITY_RANK_SET,JSONUtil.toJsonStr(rank),0);
        stringRedisTemplate.opsForZSet().add(ACTIVITY_ALL_SET,JSONUtil.toJsonStr(rank),  activity.getEndTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        saveExpire(activity);
        return Result.ok(OPERATE_DATA_SUCCESS);
    }

    @Override
    public Result getActivityById(Long id) {
        //System.out.println("id"+id);saveExpire(id);;

//        Activity activity = cacheClient.queryWithLogicalExpire(ACTIVITY_ID_KEY, id, Activity.class,ACTIVITY_LOCK_ID, ids -> getById(ids), ACTIVITY_ID_TTL, TimeUnit.MINUTES);
        Activity activity = cacheClient.queryWithPassThrough(ACTIVITY_ID_KEY, id, Activity.class, ids -> getById(ids), ACTIVITY_ID_TTL, TimeUnit.MINUTES);

        if (activity == null){
            return Result.fail(QUERY_DATA_FAIL,"活动不存在");
        }
        String jsonNum = stringRedisTemplate.opsForValue().get(ACTIVITY_NUMBER_KEY + "id");
        if (jsonNum != null){
            activity.setRegister(activity.getNumber()-Integer.valueOf(jsonNum));
        }

        if (activity.getRegisterDate().isBefore(LocalDateTime.now()) && activity.getEndTime().isAfter(LocalDateTime.now())){
            activity.setState(0);
        }else if (activity.getEndTime().isBefore(LocalDateTime.now())){
            activity.setState(1);
        }else {
            activity.setState(2);
        }

        RankData rankData = BeanUtil.copyProperties(activity, RankData.class);
        stringRedisTemplate.opsForZSet().incrementScore(ACTIVITY_RANK_SET,JSONUtil.toJsonStr(rankData),1);

        String userKey = ACTIVITY_USER_KEY + activity.getPrincipalId();
        String userJson = stringRedisTemplate.opsForValue().get(userKey);

        if(StrUtil.isNotBlank(userJson)){
            activity.setUser(JSONUtil.toBean(userJson,UserDTO.class));
        }

        if(userJson == null){
            SysUser user = userService.getById(activity.getPrincipalId());
            if(user != null){
                UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
                stringRedisTemplate.opsForValue().set(userKey,JSONUtil.toJsonStr(userDTO),ACTIVITY_USER_TTL,TimeUnit.MINUTES);
                activity.setUser(userDTO);
            }else {
                stringRedisTemplate.opsForValue().set(userKey,"",ACTIVITY_USER_TTL,TimeUnit.MINUTES);
            }
        }
        isVolunteer(activity);
        getCategory(activity);
        activity.setIsRegister(isRegister(activity.getId())?ACTIVITY_REGISTER_STATE_SUCCESS:ACTIVITY_REGISTER_STATE_FAIL);

        return Result.ok(OPERATE_DATA_SUCCESS,activity);
    }

    private Activity saveExpire(Activity activity) {
        RedisData data = new RedisData();
//        Activity activity = query().eq("id", id).one();
        data.setData(activity);
        data.setExpireTime(LocalDateTime.now().plusSeconds(1000));
        stringRedisTemplate.opsForValue().set(ACTIVITY_ID_KEY+activity.getId(),JSONUtil.toJsonStr(data));
        System.out.println("date:"+JSONUtil.toJsonStr(data));
        return activity;
    }

    private boolean isRegister(Long id) {
        Long count = activityRegisterService.query().eq("activityId", id).eq("userId", UserHolder.getUser().getId()).eq("state", ACTIVITY_REGISTER_STATE_SUCCESS).count();
        System.out.println(count);
        return BooleanUtil.isTrue(count > 0) ;
    }


    private void isVolunteer(Activity activity) {
        Volunteering volunteering = volunteerService.query().eq("activityId", activity.getId()).one();
        if(volunteering != null){
            volunteering.setIsRegister(volunteerService.isRegister(volunteering.getId()));
            activity.setVolunteering(volunteering);
        }
    }

    @Override
    public Result updateActivity(Activity activity) {
        Activity ids = getById(activity.getId());
        if(ids == null){
            return Result.fail(QUERY_DATA_FAIL,"活动不存在");
        }
        boolean isSuccess = updateById(activity);
        if(!isSuccess){
            return Result.fail(UPDATE_DATA_FAIL,"更新活动失败");
        }
        String key = ACTIVITY_ID_KEY + activity.getId();
        stringRedisTemplate.delete(key);
        saveExpire(activity);
        return Result.ok(OPERATE_DATA_SUCCESS);
    }

    @Override
    public Result getActivityByCondition(ActivityVTO activityVTO) {
        QueryWrapper<Activity> list = new QueryWrapper<>();

        if(activityVTO.getId() != null){
            list.eq("id",activityVTO.getId());
        }
        if (StrUtil.isNotBlank(activityVTO.getSpecies())){
            list.eq("species",activityVTO.getSpecies());
        }
        if (activityVTO.getRegisterDate() != null){
            list.ge("registerDate",activityVTO.getRegisterDate());
        }
        if(activityVTO.getBeginTime() != null){
            list.ge("beginTime",activityVTO.getBeginTime());
        }
        if(activityVTO.getEndTime() != null){
            list.le("endTime",activityVTO.getEndTime());
        }

        Page<Activity> page = baseMapper.selectPage(new Page<>(activityVTO.getPageIndex(), activityVTO.getPageSize()), list);
        List<ActivityDTO> records = page.getRecords().stream()
                .map(activity -> toActivityDTO(activity)).collect(Collectors.toList());
        return Result.ok(OPERATE_DATA_SUCCESS,records);
    }

    @Override
    public Result getAllList(ActivityVTO activityVTO) {
        QueryWrapper<Activity> list = new QueryWrapper<>();

        if(activityVTO.getId() != null){
            list.eq("id",activityVTO.getId());
        }
        if (StrUtil.isNotBlank(activityVTO.getSpecies())){
            list.eq("species",activityVTO.getSpecies());
        }
        if (activityVTO.getRegisterDate() != null){
            list.ge("registerDate",activityVTO.getRegisterDate());
        }
        if(activityVTO.getBeginTime() != null){
            list.ge("beginTime",activityVTO.getBeginTime());
        }
        if(activityVTO.getEndTime() != null){
            list.le("endTime",activityVTO.getEndTime());
        }

        Page<Activity> page = baseMapper.selectPage(new Page<>(activityVTO.getPageIndex(), activityVTO.getPageSize()), list);

        List<Activity> records = page.getRecords().stream().map(activity -> principal(activity)).collect(Collectors.toList());
        for (Activity record : records) {
            getCategory(record);
        }
        return Result.ok(OPERATE_DATA_SUCCESS,records);
    }

    @Override
    public Result getDetail(Long id) {

        String json = stringRedisTemplate.opsForValue().get(ACTIVITY_ID_KEY + id);
        if (json == null){
            Activity activity = query().eq("id", id).one();
            if (activity == null){
                return Result.fail(OPERATE_DATA_FAIL,"活动不存在");
            }
            json = JSONUtil.toJsonStr(activity);
            stringRedisTemplate.opsForValue().set(ACTIVITY_ID_KEY + id,json,ACTIVITY_ID_TTL,TimeUnit.MINUTES);
        }
        Activity activity = JSONUtil.toBean(json, Activity.class);
        RankData rankData = BeanUtil.copyProperties(activity, RankData.class);
        stringRedisTemplate.opsForZSet().incrementScore(ACTIVITY_RANK_SET,JSONUtil.toJsonStr(rankData),1);

        SysUser user = userService.query().eq("id", activity.getPrincipalId()).one();
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        activity.setUser(userDTO);
        getCategory(activity);
        return Result.ok(OPERATE_DATA_SUCCESS,activity);
    }

    @Override
    public Result getRanking(Integer size, Integer index) {
        List<String> collect = stringRedisTemplate.opsForZSet().reverseRange(ACTIVITY_RANK_SET, size * (index - 1), size * index - 1).stream().collect(Collectors.toList());
        System.out.println(collect);
        List<RankData> rank = collect
                .stream()
                .map(idx -> JSONUtil.toBean(idx, RankData.class))
                .collect(Collectors.toList());
        if (rank == null){
            return Result.fail(OPERATE_DATA_FAIL,"查询失败，请重新查询");
        }
        return Result.ok(OPERATE_DATA_SUCCESS,rank);
    }

    @Override
    public Result getPhotos(Integer size, Integer index) {
        QueryWrapper<Activity> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("beginTime");
        queryWrapper.isNotNull("fileName");

        Page<Activity> activityPage = baseMapper.selectPage(new Page<>(index, size), queryWrapper);
        List<PhotoUtil> list = activityPage.getRecords().stream()
                .map(idx -> BeanUtil.copyProperties(idx, PhotoUtil.class))
                .collect(Collectors.toList());
        if(list == null){
            return Result.fail(OPERATE_DATA_FAIL,"查询失败");
        }
        return Result.ok(OPERATE_DATA_SUCCESS,list);
    }

    private Activity principal(Activity activity) {
        Long id = activity.getPrincipalId();
        SysUser user = userService.query().eq("id", id).one();
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        activity.setUser(userDTO);
        return activity;
    }

    private ActivityDTO toActivityDTO(Activity activity) {
        ActivityDTO activityDTO = BeanUtil.copyProperties(activity, ActivityDTO.class);
        if (activity.getRegisterDate().isBefore(LocalDateTime.now())&& activity.getEndTime().isAfter(LocalDateTime.now())){
            activityDTO.setState(0);
        }else if (activity.getEndTime().isBefore(LocalDateTime.now())){
            activityDTO.setState(1);
        }else {
            activityDTO.setState(2);
        }
        return activityDTO;
    }

    private void getCategory(Activity activity){
        Long id = activity.getSpeciesNum();
        Menu menu = cacheClient.queryWithPassThrough(ACTIVITY_CATEGORY_KEY, id, Menu.class, ids -> menuService.getById(ids), ACTIVITY_CATEGORY_TTL, TimeUnit.MINUTES);
        if(menu != null){
            activity.setSpecies(menu.getValue());
        }
    }
}


